《windows PE》重定位表

预备知识:代码重定位

重定位的提出

         代码重定位是把可执行代码从内存的一块区域移动到另外一块地方。但是如果指令中某些操作数没有随着地址的改变而改变,这样势必导致运行出错。如下代码:我们发现全局变量的地址包含在机器码中,而局部变量没有包含绝对地址。

1
2
3
4
5
6
push ebp
mov ebp,esp
add esp,ffffffc
mov eax,dword ptr [00400FFC] //全局变量
mov eax,dword ptr [ebp-04] //局部变量
mov eax,dword ptr [evp+08] //局部变量

         重定位信息是在编译时期由编译器生成,并且保存在应用程序中,在程序执行的时候由操作系统予以修正。如果在装载时该位置已经被别的应用程序使用,操作系统会重新选择一个新的基地址。此时,就需要对所有重定位信息进行纠正,纠正的依据就是PE中的重定位表

PE文件中的重定位表

1.重定位表的定位

         重定位表位于数据目录中的第六个数据目录中。位置定位方法和前面的导入表和导出表一样。

2.重定位表项IMAGE_BASE_RELOCATION

         和导入表一样,重定位表指针指向的位置是一个数组。每个数组代表的是每一个内存页的重定位信息。也就是说每个内存页的重定位信息是不同的。下面是对于IMAGE_BASE_RELOCATION结构体的介绍:

1
2
3
4
5
6
7
IMAGE_BASE_RELOCATION STRUC 【基址重定位位于数据目录表的第六项,共8+N字节】
{
+00 h DWORD VirtualAddress ;重定位数据开始的RVA 地址
+04 h DWORD SizeOfBlock ;重定位块得长度,标识重定向字段个数
+08 h WORD TypeOffset ;重定项位数组相对虚拟RVA,个数动态分配
};
IMAGE_BASE_RELOCATION ENDS

  • VirtualAddress:双字,表示的是重定位块的RVA。本来一个地址需要4个字节,因为一个内存页大小诶1000h,也就是2的12次,所以只需要2个字节即可
  • SizeOfBlock:双字,重定位表项中的重定位块的个数。这些数组(表项)可能不是相邻的。
  • TypeOffset:表示重定位表项的类型:高四位表示的是类型。低十二位表示的重定位地址。

             重定位块的大小:n*12+4+4

3.重定位表的结构

         重定位表中包含有重定位块,重定位块中包含有重定位表项和前面的VirtualAddress,SizeOfBlock,TypeOffset字段(TypeOffset是一个数组,它的元素个数就是( SizeOfBlock - 8 ) / 2 ,TypeOffset 每一个元素占用两个字节即16位,其中高4位表示重定位类型(一般都为3),低12位表示重定位地址。)。

         所有的重定位块最终以VirtualAddress字段为0的IMAGE_BASE_RELOCATION结构为结束标志。

4.实例分析

         如下图,我们知道第一个重定位块位于0x1c00,代码起始页面RVA为00001000,块的大小为000000E8。后面的是每个重定位表项的相对位置。根据以下计算公式得到最终的实际地址。
VA=基地址(程序基地址)+代码起始页面RVA+低12位虚拟地址。
VA=01000000+00001000+009